// PerlinNoiseDlg.cpp : implementation file
//

#include "stdafx.h"
#include "TerrainEditor.h"
#include "PerlinNoiseDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPerlinNoiseDlg dialog


CPerlinNoiseDlg::CPerlinNoiseDlg(CWnd* pParent, CTerrainEditorDoc *pDoc) 
: CDialog(CPerlinNoiseDlg::IDD, pParent)
{
	m_pDoc = pDoc;

	//{{AFX_DATA_INIT(CPerlinNoiseDlg)
	//}}AFX_DATA_INIT
}


void CPerlinNoiseDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CPerlinNoiseDlg)
	DDX_Control(pDX, IDC_CHECK_ReSeed, m_ReSeed);
	DDX_Control(pDX, IDC_PROGRESS_PNoiseAlgorithm, m_GenerateProgress);
	DDX_Control(pDX, IDC_EDIT_Offset, m_Offset);
	DDX_Control(pDX, IDC_EDIT_Octaves, m_Octaves);
	DDX_Control(pDX, IDC_EDIT_Lacunarity, m_Lacunarity);
	DDX_Control(pDX, IDC_EDIT_Gain, m_Gain);
	DDX_Control(pDX, IDC_EDIT_FractalIncrement, m_H);
	DDX_Control(pDX, IDC_COMBO_BlockSize, m_BlockSize);
	DDX_Control(pDX, IDC_COMBO_fBmMethod, m_fBmMethod);
	DDX_Control(pDX, IDC_BUTTON_GeneratePerlinNoise, m_GeneratePerlinNoise);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CPerlinNoiseDlg, CDialog)
	//{{AFX_MSG_MAP(CPerlinNoiseDlg)
	ON_BN_CLICKED(IDC_BUTTON_GeneratePerlinNoise, OnButtonGeneratePerlinNoise)
	ON_CBN_SELCHANGE(IDC_COMBO_fBmMethod, OnComboSelChangefBmMethod)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPerlinNoiseDlg message handlers

BOOL CPerlinNoiseDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	m_fBmMethod.SelectString(-1, "fBm");
	m_BlockSize.SelectString(-1, "256x256");
	m_ReSeed.SetCheck(1);
	m_H.SetWindowText("1");
	m_Lacunarity.SetWindowText("2");
	m_Octaves.SetWindowText("7");
	m_Offset.SetWindowText("1");
	m_Gain.SetWindowText("2");
	m_Offset.EnableWindow(FALSE);
	m_Gain.EnableWindow(FALSE);
	m_GenerateProgress.SetPos(0);
	m_GenerateProgress.SetStep(1);
	
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CPerlinNoiseDlg::OnComboSelChangefBmMethod() 
{
	// Find out what fBm Method was chosen and enable/disable the appropriate controls
	
	int nMethod = m_fBmMethod.GetCurSel();

	switch(nMethod)
	{
		case 0: // fBm
			m_Offset.EnableWindow(FALSE);
			m_Gain.EnableWindow(FALSE);
			break;
		case 1: // fBm Turbulence
			m_Offset.EnableWindow(FALSE);
			m_Gain.EnableWindow(FALSE);
			break;
		case 2: // Multifractal
			m_Offset.EnableWindow(TRUE);
			m_Gain.EnableWindow(FALSE);
			break;
		case 3: // HeteroMultiFractal
			m_Offset.EnableWindow(TRUE);
			m_Gain.EnableWindow(FALSE);
			break;
		case 4: // HybridMultiFractal
			m_Offset.EnableWindow(TRUE);
			m_Gain.EnableWindow(FALSE);
			break;
		case 5: // RidgedMultiFractal
			m_Offset.EnableWindow(TRUE);
			m_Gain.EnableWindow(TRUE);
			break;
		default:
			break;
	}
}

void CPerlinNoiseDlg::OnButtonGeneratePerlinNoise() 
{
	// Retrieves user data from dialog box and updates member variables

	UpdateData();

	// Allocate memory for new heightmap

	int nBlockMethod = m_BlockSize.GetCurSel();

	switch(nBlockMethod)
	{
		case 0: // 64x64
			m_pDoc->m_Heightmap.Create(64);
			break;
		case 1: // 128x128
			m_pDoc->m_Heightmap.Create(128);
			break;
		case 2: // 256x256
			m_pDoc->m_Heightmap.Create(256);
			break;
		case 3: // 512x512
			m_pDoc->m_Heightmap.Create(512);
			break;
		case 4: // 1024x1024
			m_pDoc->m_Heightmap.Create(1024);
			break;
		case 5: // 2048x2048
			m_pDoc->m_Heightmap.Create(2048);
			break;
		default:
			break;
	}

	// Allocate memory for noise data

	int	nSize = m_pDoc->m_Heightmap.m_HeightValues.m_pTGAFileHeader->Image.Width;
	int	nTotalBytes = m_pDoc->m_Heightmap.m_HeightValues.m_nTotalImageBytes;
	int	nTotalPixels = nSize * nSize;
	int	nBytesPerPixel = m_pDoc->m_Heightmap.m_HeightValues.m_nBytesPerPixel;
	float *pHeights;
	pHeights = new float[nTotalPixels];

	// Set progress bar range

	m_GenerateProgress.SetRange32(0, nSize);

	// If you don't reseed you will generate same terrain each time and you can
	// test different parameter values effect...

	if (m_ReSeed.GetCheck()) m_fBm.m_PNoise.ReSeed();

	// Fill in heightmap values

	int i, j, k = 0;
	int	nMethod	= m_fBmMethod.GetCurSel();
	float H	= m_H.ReturnFloat();
	float Lac = m_Lacunarity.ReturnFloat();
	float Oct = m_Octaves.ReturnFloat();
	float Off = m_Offset.ReturnFloat();
	float G	= m_Gain.ReturnFloat();
	vector3f v;

	for (i=0; i<nSize; i++)
	{
		m_GenerateProgress.StepIt();

		for (j=0; j<nSize; j++, k++)
		{
			v.x = ((float)i / (float)nSize) + 1.0f;
			v.y = ((float)j / (float)nSize) + 1.0f;
			v.z = 0.5;

			switch(nMethod)
			{
				case 0:
					pHeights[k] = m_fBm.fBm(v, H, Lac, Oct);
					break;
				case 1:
					pHeights[k] = m_fBm.fBmTurbulence(v, H, Lac, Oct);
					break;
				case 2:
					pHeights[k] = m_fBm.MultiFractal(v, H, Lac, Oct, Off);
					break;
				case 3:
					pHeights[k] = m_fBm.HeteroMultiFractal(v, H, Lac, Oct, Off);
					break;
				case 4:
					pHeights[k] = m_fBm.HybridMultiFractal(v, H, Lac, Oct, Off);
					break;
				case 5:
					pHeights[k] = m_fBm.RidgedMultiFractal(v, H, Lac, Oct, Off, G);
					break;
				default:
					break;
			}
		}
	}

	// Scale height values between 0 and 255 for easy storage as a greyscale image

	float min, max;
	min = max = pHeights[0];

	for (i=0; i<nTotalPixels; i++)
	{
		float val = pHeights[i];
		if (min > val) min = val;
		if (max < val) max = val;
	}

	float avoid_div = 1.0f / (max - min);
	int nPixelHeight;

	for (i=0; i<nTotalBytes; i+=nBytesPerPixel)
	{
		nPixelHeight = (int)(((pHeights[i] - min) * avoid_div) * 255.0f);
		m_pDoc->m_Heightmap.m_HeightValues.m_pImageData[i] = nPixelHeight;
	}

	// Free memory

	delete pHeights;

	// Reset progress bar to zero

	m_GenerateProgress.SetPos(0);

	// Refresh all views

	m_pDoc->m_bTerrainDataExists = true;
	m_pDoc->m_b2DNewTerrainGenerated = true;
	m_pDoc->m_b3DNewTerrainGenerated = true;
	m_pDoc->UpdateAllViews(NULL);
}
